#include "DemuxerTask.h"
#include "videostreamimpl.h"
#include "audiostreamimpl.h"
#include "libavinclude.h"
#include "Receive_handler.h"
#include "Task_Base/CriticalSection.h"

#include <android/log.h>
#define LOG_TAG "Hello.my.out"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)

enum ENUM_OPERATION
{
    ENUM_STREAM_DATA,
    ENUM_STREAM_STOP,
    ENUM_STREAM_CLOSE,
};

typedef struct
{
    int                      code;
}Operation_Struct;

DemuxerTask::DemuxerTask():
    pFormatCtx(0),
    m_oAudioAbstract(0),
    m_oMediaImagehandler(0)
{
    av_register_all();
    avformat_network_init();

     oManSection = new CCriticalSection;
}

DemuxerTask::~DemuxerTask()
{
    delete oManSection;
}

int decode_interrupt_cb(void *ctx)
{
	return *((int*)ctx);
}

int read_buffer(void *opaque, uint8_t *buf, int buf_size)
{
	return ((DemuxerTask*)opaque)->recv_buffer(buf, buf_size);
}

int DemuxerTask::recv_buffer(unsigned char *buf, int buf_size)
{
	if (bExit == true)
		return false;

	while(true)
	{
        if (!m_buffer.empty())
		{
            int buff_Len = m_buffer.size();
            if( buff_Len > buf_size)
            {
                memcpy(buf, m_buffer.data(), buf_size);
                m_buffer = std::string(m_buffer.begin() + buf_size, m_buffer.end());
                return buf_size;
            }else
            {
                memcpy(buf, m_buffer.data(), buff_Len);
                m_buffer.clear();
                return buff_Len;
            }
		}

		std::string str = "";
		if (getq(str))
		{
			if (str.size() == 0)
				return 0;

            const char *pContext    = str.data();
            int			len         = str.size();

            Operation_Struct *pStruct = (Operation_Struct *)pContext;
            switch (pStruct->code)
            {
            case ENUM_STREAM_DATA:
                m_buffer.append(std::string(pContext+sizeof(Operation_Struct),len-sizeof(Operation_Struct)));
                break;

            case ENUM_STREAM_STOP:
                CloseStreamTask();
                break;

            case ENUM_STREAM_CLOSE:
                CloseStreamTask();
                bExit = true;
                return false;
            }
		}
	}
}

void DemuxerTask::stream_data_message(const char *data, int size)
{
    Operation_Struct peration_Struct;
    peration_Struct.code = ENUM_STREAM_DATA;

    std::string str_buf;
    str_buf.append((char*)&peration_Struct, sizeof(peration_Struct));
    str_buf.append(data, size);
    putq(str_buf);
}

void DemuxerTask::stream_stop_message()
{
    Operation_Struct peration_Struct;
    peration_Struct.code = ENUM_STREAM_STOP;

    std::string str_buf;
    str_buf.append((char*)&peration_Struct, sizeof(peration_Struct));
    putq(str_buf);
}
void DemuxerTask::stream_close_message()
{
    Operation_Struct peration_Struct;
    peration_Struct.code = ENUM_STREAM_CLOSE;

    std::string str_buf;
    str_buf.append((char*)&peration_Struct, sizeof(peration_Struct));
    putq(str_buf);
}

bool DemuxerTask::open(const std::string &url, AudioAbstract *event, MediaImagehandler *handler)
{
    discard();
    m_buffer.clear();
    bExit               = false;
    m_oAudioAbstract    = event;
    m_oMediaImagehandler= handler;

 	LOGD("DemuxerTask::open%s \n",url.data());

	pFormatCtx = avformat_alloc_context();
	pFormatCtx->interrupt_callback.callback = decode_interrupt_cb;
    pFormatCtx->interrupt_callback.opaque   = &bExit;

	if (url.empty())
	{
        unsigned char *aviobuffer = (unsigned char *)av_malloc(32768);
        AVIOContext *avio = avio_alloc_context(aviobuffer, 32768, 0, this, read_buffer, NULL, NULL);
		pFormatCtx->pb = avio;
	}
	int err_code;
	if ((err_code = avformat_open_input(&pFormatCtx, url.empty() ? NULL : url.data(), NULL, NULL)) != 0)
	//if ((err_code = avformat_open_input(&pFormatCtx, "/sdcard/cuc_ieschool.flv", NULL, NULL)) != 0)
	{
		char buf[1024] = "";
		av_strerror(err_code, buf, 1024);

		LOGD("/sdcard/cuc_ieschool.flv err 1 %s\n",buf);
		return false;
	}

	for (int i = 0; i < pFormatCtx->nb_streams; i++)
	{
		if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
		{
            return true;
		}
	}
LOGD("err 2");
    return  false;
}

void DemuxerTask::close()
{

LOGD("err close0");
    bExit = true;

	wait();
LOGD("err close1");
    bExit = false;
	//--------------------------
    CloseStreamTask();
    //---------------------------
	if (pFormatCtx)
	{
		avformat_close_input(&pFormatCtx);
		avformat_free_context(pFormatCtx);
		pFormatCtx = NULL;
    }
}

int DemuxerTask::GetAudio(char *buf, int len)
{
    if(len <= 0 )
        return 0;
    CCriticalAutoLock loAutoLock(*oManSection);
    AudioStreamImpl *Audio =  (AudioStreamImpl *)findAudio();
    if(Audio)
        return Audio->GetDecodeAudio(buf,len);
    return 0 ;
}

int DemuxerTask::svc()
{
	AVPacket packet;
	av_init_packet(&packet);

	while (true)
	{
		int ret = av_read_frame(pFormatCtx, &packet);

		if (ret >= 0)
		{
            std::map<int, StreamAbstract *>::iterator find = m_oManStream.find(packet.stream_index);

            if(find != m_oManStream.end())
            {
                ((StreamAbstract *)(find->second))->receive_handler((char*)&packet, sizeof(AVPacket));
            }else
            {
                CCriticalAutoLock loAutoLock(*oManSection);

                StreamAbstract * newStream = NULL;

                if(pFormatCtx->streams[packet.stream_index]->codec->codec_type == AVMEDIA_TYPE_AUDIO )
                {
                     newStream = new AudioStreamImpl(pFormatCtx->streams[packet.stream_index],m_oAudioAbstract);

                }else if(pFormatCtx->streams[packet.stream_index]->codec->codec_type == AVMEDIA_TYPE_VIDEO )
                {
                    newStream = new VideoStreamImpl(pFormatCtx->streams[packet.stream_index],m_oMediaImagehandler);
                }

                if(newStream)
                {
                    newStream->receive_handler((char*)&packet, sizeof(AVPacket));
                    m_oManStream.insert(make_pair(packet.stream_index,newStream));
                }

                this->StatusUpdates();
            }
			if (bExit == true)
			{
				break;
			}
		}
		else
		{
			if (ret == AVERROR_EOF)
			{
				break;
			}
		}
	}
    LOGD("return DemuxerTask::svc \n");
    return 0;
}

void DemuxerTask::StatusUpdates()
{
    AudioStreamImpl *Audio =  (AudioStreamImpl *)findAudio();
    VideoStreamImpl *video =  (VideoStreamImpl *)findVideo();
    if(Audio &&(Audio->is_init() == false))
          Audio->Init();

    if(Audio && video)
    {
        if(video->is_init() == false)
        {
            video->SetTimerSynchTarget(Audio);
            video->Init();
        }
    }
}

void DemuxerTask::CloseStreamTask()
{
    CCriticalAutoLock loAutoLock(*oManSection);

    StreamAbstract * vStreamAbstract = findVideo();
    StreamAbstract * aStreamAbstract = findAudio();
    if(vStreamAbstract)
        vStreamAbstract->UnInit();

    if(aStreamAbstract)
        aStreamAbstract->UnInit();

    std::map<int, StreamAbstract *>::iterator find = m_oManStream.begin();
    while(find != m_oManStream.end())
    {
        if(find->second)
            delete find->second;

        m_oManStream.erase(find);
        find =  m_oManStream.begin();
    }
}

StreamAbstract *DemuxerTask::findAudio()
{
     std::map<int, StreamAbstract *>::iterator find = m_oManStream.begin();
     while(find != m_oManStream.end())
     {
        if(((StreamAbstract *)(find->second))->GetStreamType() == AVMEDIA_TYPE_AUDIO)
            return find->second;
        find++;
     }
     return 0;
}

StreamAbstract *DemuxerTask::findVideo()
{
    std::map<int, StreamAbstract *>::iterator find = m_oManStream.begin();
    while(find != m_oManStream.end())
    {
       if(((StreamAbstract *)(find->second))->GetStreamType() == AVMEDIA_TYPE_VIDEO)
           return find->second;
       find++;
    }
    return 0;
}

